/*
Copyright (C) 2015 Marien Raat, Jochem Raat

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <SFML/Graphics.hpp>
#include <random>

#include <diamondsquare.h>
#include <islandHeightmapGenerator.h>
#include <perlinNoise.h>
#include <fractalNoise.h>
#include <landMap.h>
#include <windMap.h>
#include <heatMap.h>

#include <normalizer.h>

void saveDiamondSquareMap(const char *filename);
void saveIslandHeightMap(const char *filename);
void saveLandMap(const char *);
void saveWindMap(const char *);
void saveHeatMap(const char *);
void savePerlinNoiseMap(const char *filename);
void saveFractalNoiseMap(const char *filename);
void saveSpecialFractalMap(const char *filename);

void saveValueMap(double *values, unsigned width, unsigned height,
                  const char *filename);

std::random_device rd;
std::mt19937 mt(rd());
DiamondSquareGenerator dsg(&mt);
IslandHeightmapGenerator ihmg(&mt);
LandMapGenerator lmg;
WindMapGenerator wmg;
HeatMapGenerator htmg(&mt);
unsigned size = 1024;

int main()
{
    saveDiamondSquareMap("testnoise/diamondsquare.png");
    saveIslandHeightMap("testnoise/islandheightmap.png");
    saveLandMap("testnoise/landmap.png");
    saveWindMap("testnoise/windmap.png");
    saveHeatMap("testnoise/heatmap.png");
    savePerlinNoiseMap("testnoise/perlinNoise.png");
    saveFractalNoiseMap("testnoise/fractalNoise.png");
    saveSpecialFractalMap("testnoise/specialFractal.png");
    return 0;
}

void saveDiamondSquareMap(const char *filename)
{
    int size = 1025;
    DiamondSquareMapSettings dsSettings;
    dsSettings.size = size;
    dsSettings.h = 0.5;
    dsSettings.mapType = MAPTYPE_LAND;
    dsSettings.overwriteOldMap = true;
    dsSettings.modifierFunction = logaritmicDisplacementModifier;

    dsg.createNewMap(dsSettings);
    dsg.finishMap();

    Normalizer<double>::normalize(dsg.map, dsg.map, size * size,
                                  0.0, 1.0);

    saveValueMap(dsg.map, size, size, filename);
}

void saveIslandHeightMap(const char *filename)
{
    int diamondSquareSize = (int) pow(2.0, ceil
                                  (log(size - 1) /
                                   log(2.0))) + 1,
        amountOfPasses = (int) (log(diamondSquareSize) /
                                log(2.0));
    double * heightmapModifiers = new double[amountOfPasses];
    double modifiers[10] = {
        0.0, // 1024
        1.0, // 512
        0.9, // 256
        0.55, // 128
        0.35, // 64
        0.2, // 32
        0.1, // 16
        0.05, // 8
        0.02, // 4
        0.004, // 2
    };
    if (amountOfPasses > 10) {
        for (int i = 0; i < amountOfPasses; i++) {
            heightmapModifiers[i] = i < (amountOfPasses - 10) ?
                                        0.0 : modifiers[i - (amountOfPasses - 10)];
        }
    } else {
        for (int i = 10 - amountOfPasses; i < 10; i++) heightmapModifiers[i] = modifiers[i];
    }


    ihmg.createNewMap(size, size, heightmapModifiers);

    saveValueMap(ihmg.islandMap, size, size, filename);
}

void saveLandMap(const char *filename)
{
    lmg.setWaterLevel(0.45);

    lmg.generateSurfaceHeightmap(ihmg.islandMap, size, size, true);
    double *landValues = new double[size * size];

    for (int i = 0; i < size * size; i++)
        landValues[i] = lmg.landMap[i] ? 1.0 : 0.0;

    saveValueMap(landValues, size, size, filename);
}

void saveWindMap(const char *filename)
{
    double *windMap = wmg.generateWindMap(lmg.surfaceHeightmap, size, size);

    saveValueMap(windMap, size, size, filename);
}

void saveHeatMap(const char *filename)
{
    double *heatMap = htmg.generateHeatMap(ihmg.islandMap, size, size);

    saveValueMap(heatMap, size, size, filename);
}

void savePerlinNoiseMap(const char *filename)
{
    PerlinNoiseGenerator png(&mt);
    int width = 1024, height = 1024;
    double *map = png.generatePerlinNoiseMap(
        width, height, 64);
    saveValueMap(map, width, height, filename);
}

void saveFractalNoiseMap(const char *filename)
{
    FractalNoiseGenerator fng(&mt);
    int width = 1024, height = 1024;
    fng.generateStandardFractalNoise(width, height, 0.45);
    Normalizer<double>::normalize(fng.noiseMap, fng.noiseMap, width * height,
                                 0.0, 1.0);
    saveValueMap(fng.noiseMap, width, height, filename);
}

void saveSpecialFractalMap(const char* filename) {
    FractalNoiseGenerator fng(&mt);
    int width = 1024, height = 1024;
    int passes = 9;
    double values [18] = {
        512.0, 0.5,
        256.0, 0.8,
        128.0, 0.6,
        64.0,  0.4,
        32.0,  0.2,
        16.0,  0.08,
        8.0,   0.02,
        4.0,   0.003,
        2.0,   0.0001};
    fng.generateFractalNoiseFromValues(values, passes * 2, width, height);
    Normalizer<double>::normalize(fng.noiseMap, fng.noiseMap, width * height,
                                 0.0, 1.0);

    saveValueMap(fng.noiseMap, width, height, filename);
}

void saveValueMap(double *values, unsigned width, unsigned height,
                  const char *filename)
{
    sf::Image i;
    i.create(width, height);
    for (int x = 0; x < width; x++)
        for (int y = 0; y < height; y++) {
            int value = (int) 255.0 * values[x + y * width];
            i.setPixel(x, y, sf::Color(value, value, value));
        }
    i.saveToFile(filename);
}

